pub fn learn_ownership() {
  // _learn_variable_scope();
  _learn_ownership_fn0();
  _learn_ownership_fn1();
  _learn_ref_0();
  _learn_ref_1();
}

// 栈以放入值的顺序存储值,并以相反的顺序取出值. 即后进先出(last in, first out)LIFO.
// 栈中所有数据都必须占用已知且固定的大小.在编译大小未知或大小可能发生变化的数据时,要改为存储在堆上.
// 入栈比在堆上分配内存要快,因为入栈时分配器无需为存储新数据去搜索内存空间;其位置总是在栈顶.
// 访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问.(指针一般存储在栈上)

// 跟踪哪部分代码正在使用堆上的哪些数据,最大限度的减少堆上的重复数据的数量,以及清理堆上不再使用的数据
// 确保不会耗尽空间,这些问题正是所有权系统要处理的.
// 所有权的主要目的就是管理堆数据.

// 所有权规则
// 1,rust中的每一个值都有一个所有者(owner).
// 2,值在任意时刻有且只有一个所有者.
// 3,当所有者(变量)离开作用域,这个值将被丢弃.

// 变量作用域
// 作用域是一个项(item)在程序中有效的范围.
fn _learn_variable_scope() {
  // println!("{s}"); s 在这里无效,尚未声明
  let s = "hello"; // 从此处起，s是有效的
  {
    let a = "world";
    println!("{s} {a}");
  } // 此作用域结束,a不再有效
    // println!("{a}");
  println!("{s}");
} // 此作用域结束,s不再有效

fn _learn_ownership_fn0() {
  let s = String::from("hello");
  // 1,将s的所有权转移到函数中
  // 3,通过函数调用表达式赋值给新的s,得到所有权
  let s = _takes_ownership(s);
  // 有所有权,可以输出
  println!("{}", s);
  println!("======",);
}

fn _takes_ownership(s: String) -> String {
  // 2,获得所有权,然后通过返回值返回到函数调用表达式
  s
}

// 使用可变变量移动所有权
fn _learn_ownership_fn1() {
  let mut s = String::from("hello");
  s = _takes_ownership(s);
  println!("{}", s);
  println!("======",);
}

fn _learn_ownership_fn2() {
  let mut s = String::from("hello");
  //s = _takes_ownership_mut(&mut s);
  println!("{}", s);
}

fn _cal_len(s: &String) -> usize {
  s.len()
} // s 是引用,没有所有权,在这里离开了作用域,但什么都不会发生

fn _learn_ref_0() {
  let s = String::from("hello");
  // 引用不拥有值的所有权,所以引用停止使用时,也不会丢弃值
  let len = _cal_len(&s); // &s 创建一个指向s的引用(s是指向堆上字符串"hello"的包含裸指针的智能指针)
  println!("{s}.len={len}");
}

// 创建一个引用的行为就是借用(borrowing).如现实生活中,一个人拥有某个东西,你去借来,用完后要还回去.
fn _learn_ref_1() {
  // let s = String::from("hello");
  // _change0(&mut s); // 不能将不可变引用改为可变的
  let mut s = String::from("hello");
  // _change0(&s); // 期待可变引用,却给了不可变引用
  _change0(&mut s); // ok

  let a = &mut s;
  let b = &mut s; // 可变的s不能借用超过一次
                  // println!("{a}, {b}") // 同时使用a,b,引起b定义所在行编译报错
                  // println!("{a}"); // 使用a,引起b定义所在行编译报错
  println!("{b}"); // ok,只用b,编译通过
  b.push_str(" ok");
  println!("{b}"); //
}
// 引用的规则:
// 同一时刻,要么有多个不可变引用,要么只有一个可变引用.
// 不可变引用和可变引用不能同时存在.
// 引用必须总是有效的

fn _change0(s: &mut String) {
  // s.push_str("end") // 不可变借用,不能修改值
  s.push_str(" end") // 只有可变引用才能修改值
}
